<template>
  <div class="virtual-table" ref="containerRef">
    <div class="table-header" v-if="showHeader">
      <div 
        v-for="column in columns" 
        :key="column.field"
        class="header-cell"
        :style="{ width: column.width || '150px' }">
        <span>{{ column.name }}</span>
        <el-icon v-if="sortable" @click="handleSort(column.field)" class="sort-icon">
          <Sort />
        </el-icon>
      </div>
    </div>
    
    <div 
      class="table-body" 
      ref="scrollRef"
      @scroll="handleScroll"
      :style="{ height: `${containerHeight}px` }">
      
      <div 
        class="virtual-spacer-top" 
        :style="{ height: `${offsetY}px` }">
      </div>
      
      <div 
        v-for="(item, index) in visibleItems" 
        :key="getItemKey(item, startIndex + index)"
        class="table-row"
        :class="{ 
          'selected': selectedItems.includes(getItemKey(item, startIndex + index)),
          'hover': hoveredIndex === startIndex + index 
        }"
        :style="{ height: `${itemHeight}px` }"
        @click="handleRowClick(item, startIndex + index)"
        @mouseenter="hoveredIndex = startIndex + index"
        @mouseleave="hoveredIndex = -1">
        
        <div 
          v-for="column in columns" 
          :key="column.field"
          class="table-cell"
          :style="{ width: column.width || '150px' }">
          
          <slot 
            :name="column.field" 
            :item="item" 
            :value="item[column.field]"
            :index="startIndex + index">
            <span :class="getCellClass(item[column.field], column)">
              {{ formatCellValue(item[column.field], column) }}
            </span>
          </slot>
        </div>
      </div>
      
      <div 
        class="virtual-spacer-bottom" 
        :style="{ height: `${totalHeight - offsetY - visibleHeight}px` }">
      </div>
    </div>
    
    <div class="table-footer" v-if="showFooter">
      <div class="footer-info">
        显示 {{ startIndex + 1 }} - {{ Math.min(endIndex, totalItems) }} 条，共 {{ totalItems }} 条
      </div>
      <div class="footer-actions">
        <el-button size="small" @click="scrollToTop">回到顶部</el-button>
        <el-button size="small" @click="scrollToBottom">到底部</el-button>
      </div>
    </div>
  </div>
</template>

<script setup>
import { ref, computed, onMounted, onUnmounted, watch, nextTick } from 'vue'
import { Sort } from '@element-plus/icons-vue'

const props = defineProps({
  data: {
    type: Array,
    required: true
  },
  columns: {
    type: Array,
    required: true
  },
  itemHeight: {
    type: Number,
    default: 40
  },
  containerHeight: {
    type: Number,
    default: 400
  },
  buffer: {
    type: Number,
    default: 5
  },
  showHeader: {
    type: Boolean,
    default: true
  },
  showFooter: {
    type: Boolean,
    default: true
  },
  sortable: {
    type: Boolean,
    default: false
  },
  selectable: {
    type: Boolean,
    default: false
  },
  keyField: {
    type: String,
    default: 'id'
  }
})

const emit = defineEmits(['row-click', 'selection-change', 'sort-change'])

// 响应式数据
const containerRef = ref(null)
const scrollRef = ref(null)
const scrollTop = ref(0)
const hoveredIndex = ref(-1)
const selectedItems = ref([])
const sortField = ref('')
const sortDirection = ref('asc')

// 计算属性
const totalItems = computed(() => props.data.length)
const totalHeight = computed(() => totalItems.value * props.itemHeight)

const visibleCount = computed(() => 
  Math.ceil(props.containerHeight / props.itemHeight) + props.buffer * 2
)

const startIndex = computed(() => 
  Math.max(0, Math.floor(scrollTop.value / props.itemHeight) - props.buffer)
)

const endIndex = computed(() => 
  Math.min(totalItems.value, startIndex.value + visibleCount.value)
)

const visibleItems = computed(() => 
  props.data.slice(startIndex.value, endIndex.value)
)

const offsetY = computed(() => startIndex.value * props.itemHeight)
const visibleHeight = computed(() => visibleCount.value * props.itemHeight)

// 方法
const handleScroll = (event) => {
  scrollTop.value = event.target.scrollTop
}

const handleRowClick = (item, index) => {
  if (props.selectable) {
    const key = getItemKey(item, index)
    const selectedIndex = selectedItems.value.indexOf(key)
    
    if (selectedIndex > -1) {
      selectedItems.value.splice(selectedIndex, 1)
    } else {
      selectedItems.value.push(key)
    }
    
    emit('selection-change', selectedItems.value)
  }
  
  emit('row-click', item, index)
}

const handleSort = (field) => {
  if (sortField.value === field) {
    sortDirection.value = sortDirection.value === 'asc' ? 'desc' : 'asc'
  } else {
    sortField.value = field
    sortDirection.value = 'asc'
  }
  
  emit('sort-change', {
    field: sortField.value,
    direction: sortDirection.value
  })
}

const getItemKey = (item, index) => {
  return item[props.keyField] || index
}

const getCellClass = (value, column) => {
  const classes = []
  
  if (value == null || value === '') {
    classes.push('empty-cell')
  }
  
  if (column.dataType === 'number' && isNaN(Number(value))) {
    classes.push('invalid-cell')
  }
  
  return classes.join(' ')
}

const formatCellValue = (value, column) => {
  if (value == null || value === '') return '-'
  
  switch (column.dataType) {
    case 'currency':
      return `¥${Number(value).toLocaleString()}`
    case 'date':
      try {
        return new Date(value).toLocaleDateString()
      } catch {
        return value
      }
    case 'number':
      return Number(value).toLocaleString()
    default:
      return value
  }
}

const scrollToTop = () => {
  if (scrollRef.value) {
    scrollRef.value.scrollTop = 0
  }
}

const scrollToBottom = () => {
  if (scrollRef.value) {
    scrollRef.value.scrollTop = totalHeight.value
  }
}

const scrollToIndex = (index) => {
  if (scrollRef.value) {
    scrollRef.value.scrollTop = index * props.itemHeight
  }
}

const clearSelection = () => {
  selectedItems.value = []
  emit('selection-change', [])
}

const selectAll = () => {
  selectedItems.value = props.data.map((item, index) => getItemKey(item, index))
  emit('selection-change', selectedItems.value)
}

// 监听数据变化
watch(() => props.data, () => {
  scrollToTop()
  clearSelection()
}, { deep: true })

// 暴露方法
defineExpose({
  scrollToTop,
  scrollToBottom,
  scrollToIndex,
  clearSelection,
  selectAll
})
</script>

<style scoped>
.virtual-table {
  border: 1px solid var(--el-border-color);
  border-radius: 6px;
  overflow: hidden;
  background: var(--el-bg-color);
}

.table-header {
  display: flex;
  background: var(--el-fill-color-light);
  border-bottom: 1px solid var(--el-border-color);
  font-weight: 600;
  position: sticky;
  top: 0;
  z-index: 10;
}

.header-cell {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 12px 16px;
  border-right: 1px solid var(--el-border-color);
  color: var(--el-text-color-primary);
  font-size: 14px;
}

.header-cell:last-child {
  border-right: none;
}

.sort-icon {
  cursor: pointer;
  opacity: 0.6;
  transition: opacity 0.2s ease;
}

.sort-icon:hover {
  opacity: 1;
}

.table-body {
  overflow-y: auto;
  position: relative;
}

.table-row {
  display: flex;
  border-bottom: 1px solid var(--el-border-color-lighter);
  transition: background-color 0.2s ease;
  cursor: pointer;
}

.table-row:hover,
.table-row.hover {
  background: var(--el-fill-color-light);
}

.table-row.selected {
  background: var(--el-color-primary-light-9);
}

.table-cell {
  display: flex;
  align-items: center;
  padding: 8px 16px;
  border-right: 1px solid var(--el-border-color-lighter);
  font-size: 14px;
  color: var(--el-text-color-regular);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.table-cell:last-child {
  border-right: none;
}

.empty-cell {
  color: var(--el-text-color-placeholder);
  font-style: italic;
}

.invalid-cell {
  color: var(--el-color-danger);
  background: var(--el-color-danger-light-9);
  padding: 2px 4px;
  border-radius: 3px;
}

.table-footer {
  display: flex;
  justify-content: space-between;
  align-items: center;
  padding: 8px 16px;
  background: var(--el-fill-color-light);
  border-top: 1px solid var(--el-border-color);
  font-size: 12px;
  color: var(--el-text-color-secondary);
}

.footer-actions {
  display: flex;
  gap: 8px;
}

/* 滚动条样式 */
.table-body::-webkit-scrollbar {
  width: 8px;
}

.table-body::-webkit-scrollbar-track {
  background: var(--el-fill-color-lighter);
}

.table-body::-webkit-scrollbar-thumb {
  background: var(--el-fill-color-dark);
  border-radius: 4px;
}

.table-body::-webkit-scrollbar-thumb:hover {
  background: var(--el-fill-color-darker);
}

/* 响应式设计 */
@media (max-width: 768px) {
  .header-cell,
  .table-cell {
    padding: 6px 8px;
    font-size: 12px;
  }
  
  .table-footer {
    flex-direction: column;
    gap: 8px;
    align-items: stretch;
  }
  
  .footer-actions {
    justify-content: center;
  }
}
</style>
